﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Enumeration.Pnp;
using Windows.Graphics.Display;
using Windows.Storage.Streams;
using Windows.System;
using Windows.System.Profile;

namespace SharpDepend.WinRT
{
    class EnviromentStatisticsManager : IEnviromentStatisticsManager
    {
        class EnviromentStatisticsClass : IEnviromentStatistics
        {
            public class SystemInfoHelper
            {
                const string ItemNameKey = "System.ItemNameDisplay";
                const string ModelNameKey = "System.Devices.ModelName";
                const string ManufacturerKey = "System.Devices.Manufacturer";
                const string DeviceClassKey = "{A45C254E-DF1C-4EFD-8020-67D146A850E0},10";
                const string PrimaryCategoryKey = "{78C34FC8-104A-4ACA-9EA4-524D52996E57},97";
                const string DeviceDriverVersionKey = "{A8B865DD-2E3D-4094-AD97-E593A70C75D6},3";
                const string RootContainer = "{00000000-0000-0000-FFFF-FFFFFFFFFFFF}";
                const string RootQuery = "System.Devices.ContainerId:=\"" + RootContainer + "\"";
                const string HalDeviceClass = "4d36e966-e325-11ce-bfc1-08002be10318";


                public static async Task<ProcessorArchitecture> GetProcessorArchitectureAsync()
                {
                    var halDevice = await GetHalDevice(ItemNameKey);
                    if (halDevice != null && halDevice.Properties[ItemNameKey] != null)
                    {
                        var halName = halDevice.Properties[ItemNameKey].ToString();
                        if (halName.Contains("x64")) return ProcessorArchitecture.X64;
                        if (halName.Contains("ARM")) return ProcessorArchitecture.Arm;
                        return ProcessorArchitecture.X86;
                    }
                    return ProcessorArchitecture.Unknown;
                }


                public static Task<string> GetDeviceManufacturerAsync()
                {
                    return GetRootDeviceInfoAsync(ManufacturerKey);
                }


                public static Task<string> GetDeviceModelAsync()
                {
                    return GetRootDeviceInfoAsync(ModelNameKey);
                }


                public static Task<string> GetDeviceCategoryAsync()
                {
                    return GetRootDeviceInfoAsync(PrimaryCategoryKey);
                }


                public static async Task<string> GetWindowsVersionAsync()
                {
                    // There is no good place to get this. 
                    // The HAL driver version number should work unless you're using a custom HAL...  
                    var hal = await GetHalDevice(DeviceDriverVersionKey);
                    if (hal == null || !hal.Properties.ContainsKey(DeviceDriverVersionKey))
                        return null;


                    var versionParts = hal.Properties[DeviceDriverVersionKey].ToString().Split('.');
                    return string.Join(".", versionParts.Take(2).ToArray());
                }


                private static async Task<string> GetRootDeviceInfoAsync(string propertyKey)
                {
                    var pnp = await PnpObject.CreateFromIdAsync(PnpObjectType.DeviceContainer,
                                RootContainer, new[] { propertyKey });
                    return (string)pnp.Properties[propertyKey];
                }


                private static async Task<PnpObject> GetHalDevice(params string[] properties)
                {
                    var actualProperties = properties.Concat(new[] { DeviceClassKey });
                    var rootDevices = await PnpObject.FindAllAsync(PnpObjectType.Device,
                        actualProperties, RootQuery);


                    foreach (var rootDevice in rootDevices.Where(d => d.Properties != null && d.Properties.Any()))
                    {
                        var lastProperty = rootDevice.Properties.Last();
                        if (lastProperty.Value != null)
                            if (lastProperty.Value.ToString().Equals(HalDeviceClass))
                                return rootDevice;
                    }
                    return null;
                }
            }

            static EnviromentStatisticsClass()
            {
                Init();
            }

            private static string mManufacturer = "Not initialized";

            private static async void Init()
            {
                mManufacturer = await SystemInfoHelper.GetDeviceManufacturerAsync();
            }

            private StringBuilder mSB = new StringBuilder();

            public long AppMemoryUsed
            {
                get
                {
                    return GC.GetTotalMemory(false);
                    //return Process.PagedMemorySize64();
                }
            }

            public long TotalMemory
            {
                get
                {
                    return -1;
                }
            }

            public int CPUSpeed
            {
                get
                {
                    return -1;
                }
            }

            public int CPUProcessors
            {
                get
                {
                    return Environment.ProcessorCount;
                }
            }

            public string Device
            {
                get
                {
                    return mManufacturer;
                }
            }

            public double ScaleFactor
            {
                get
                {
                    var currentView = DisplayInformation.GetForCurrentView();

                    bool ok;
                    double win10_RawPixelsPerViewPixel = SharpDepend.Classes.PeformanceHelper.GetProperty<double>(typeof(DisplayInformation), "RawPixelsPerViewPixel", currentView, out ok);
                    if (ok)
                    {
                        // If running on Windows10 take the "RawPixelsPerViewPixel" instead.
                        return (float)win10_RawPixelsPerViewPixel;
                    }
                    else
                    {
                        // Windows8/RT has not that feature so just take the resolution scale for the mouse. (MainPage.xaml.cs is a bit different)
                        return (int)currentView.ResolutionScale * 0.01;
                    }
                }
            }
        }

        private static EnviromentStatisticsClass mInstance;

        public IEnviromentStatistics EnviromentStatistics
        {
            get
            {
                if (mInstance == null)
                {
                    mInstance = new EnviromentStatisticsClass();
                }

                return mInstance;
            }
        }
    }
}
